﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using WcfJsHoster;

namespace PremisPublisher.Common
{

    /*
     
     * 扫描wcf，生成前端业务使用的js脚本（不动产登记）
     * 
     * 作者：汪东
     * 修改：张鹏
     * 日期：2016.5
     
     */


    public class MessageEventArgs : EventArgs
    {
        public string Message { get; set; }

        public MessageEventArgs(string msg)
        {
            this.Message = msg;
        }
    }

    public class JsHoster : IDisposable
    {

        public event EventHandler<MessageEventArgs> OnMessageShow = delegate { };

        private bool defaultParams;
        private string domain;
        private string file;
        private StringBuilder json;
        private string path;
        private string projectName;
        private WebConfigInfo serviceList;
        private bool summary;
        private List<SvcFileInfo> svcList;
        private string outpath;



        public JsHoster(string path, bool defaultParams, bool summary, string file, string outputpath)
        {
            this.path = "";
            this.defaultParams = true;
            this.summary = true;
            this.file = "";
            this.projectName = "";
            this.domain = "";
            this.svcList = new List<SvcFileInfo>();
            this.serviceList = null;
            this.json = new StringBuilder();
            this.path = path;
            this.defaultParams = defaultParams;
            this.summary = summary;
            this.file = file;
            this.outpath = outputpath;
        }

        private void showMessage(string p)
        {
            if (this.OnMessageShow != null)
            {
                OnMessageShow(this, new MessageEventArgs(p));
            }
        }

        private void analysis(object project)
        {
            Thread.Sleep(0xbb8);
            string path = project.ToString();
            DirectoryInfo dir = new DirectoryInfo(path);
            FileInfo[] files = dir.GetFiles("*.csproj");
            if (files.Length != 1)
            {
                this.showMessage("该文件夹下没有找到项目信息");
            }
            else
            {
                string str2 = dir.FullName + @"\Web.config";
                if (!File.Exists(str2))
                {
                    this.showMessage("该文件夹下没有找到配置文件信息");
                }
                else
                {
                    this.serviceList = new WebConfigInfo(str2);
                    string name = files[0].Name;
                    name = name.Substring(0, name.LastIndexOf('.'));
                    this.projectName = name;

                    this.domain = this.getDomain(files[0].FullName);
                    List<FileInfo> list = this.getSvcFile(dir);
                    if (list.Count == 0)
                    {
                        this.showMessage("找不到服务地址");

                    }
                    else
                    {

                        foreach (FileInfo info2 in list)
                        {
                            string svcText = this.readFileText(info2.FullName);
                            string str5 = this.getServiceName(svcText);
                            if (!string.IsNullOrEmpty(str5))
                            {
                                SvcFileInfo item = new SvcFileInfo
                                {
                                    SvcFile = info2.Name,
                                    Url = this.domain + this.convertUrl(path, info2.FullName),
                                    ServiceName = str5
                                };
                                this.svcList.Add(item);

                            }
                            Thread.Sleep(100);
                        }

                        this.loadAssembly();

                        this.createJSFile();

                        if (System.IO.File.Exists(this.file))
                        {
                            new FileInfo(this.file).IsReadOnly = false;
                        }

                        FileStream stream = new FileStream(this.file, FileMode.Create, FileAccess.Write);
                        byte[] bytes = Encoding.UTF8.GetBytes(this.json.ToString());
                        stream.Write(bytes, 0, bytes.Length);
                        stream.Close();
                        this.showMessage("分析 " + System.IO.Path.GetFileName(this.path) + "... " + this.file + " 生成完成");
                        CopyHostToOutPath(this.outpath);
                        this.showMessage("拷贝 " + System.IO.Path.GetFileName(this.file) + " 至 " + this.outpath + " 完成");
                    }
                }
            }
        }

        private void loadAssembly()
        {
            string path = this.path + @"\bin\";
            DirectoryInfo info = new DirectoryInfo(path);
            APISummary.LoadAllSummaryInfo(path);
            FileInfo[] files = info.GetFiles("*.dll");
            if (files.Length != 0)
            {
                AppDomain domain = AppDomain.CreateDomain("AsynDomain");
                foreach (ServiceInfo info2 in this.serviceList.list)
                {
                    foreach (FileInfo info3 in files)
                    {
                        //解决非托管文件句柄占用问题
                        var tmp_file = System.IO.Path.GetTempFileName();
                        System.IO.File.Copy(info3.FullName, tmp_file, true);
                        System.Type type = Assembly.LoadFrom(tmp_file).GetType(info2.ServiceName);
                        //System.Type type = Assembly.LoadFrom(info3.FullName).GetType(info2.ServiceName);
                        if (type != null)
                        {
                            info2.AssemblyName = type.Assembly.FullName;
                            info2.AssemblyName = info2.AssemblyName.Substring(0, info2.AssemblyName.IndexOf(','));
                            List<EndPointInfo> endPoints = info2.EndPoints;
                            foreach (EndPointInfo info4 in endPoints)
                            {
                                System.Type type2 = type.GetInterface(info4.ContractName, true);
                                if ((type2.GetCustomAttributes(typeof(ServiceContractAttribute), false).Length != 0) && ((type2 != null) && (type2.GetCustomAttributes(typeof(ServiceContractAttribute), false).Length > 0)))
                                {
                                    MethodInfo[] methods = type2.GetMethods();
                                    foreach (MethodInfo info5 in methods)
                                    {
                                        if (info5.GetCustomAttributes(typeof(OperationContractAttribute), false).Length != 0)
                                        {
                                            WcfMethodInfo info6;
                                            ParameterInfo[] parameters;
                                            object[] customAttributes = info5.GetCustomAttributes(typeof(WebInvokeAttribute), false);
                                            object[] objArray3 = info5.GetCustomAttributes(typeof(WebGetAttribute), false);
                                            if (customAttributes.Length > 0)
                                            {
                                                WebInvokeAttribute attribute = customAttributes[0] as WebInvokeAttribute;
                                                info6 = new WcfMethodInfo
                                                {
                                                    HttpType = attribute.Method,
                                                    Name = info5.Name
                                                };
                                                parameters = info5.GetParameters();
                                                if (parameters.Length > 0)
                                                {
                                                    info6.MethodSignature = info2.ServiceName + "." + info5.Name + "(";
                                                    foreach (ParameterInfo info7 in parameters)
                                                    {
                                                        info6.MethodSignature = info6.MethodSignature + info7.ParameterType.FullName + ",";
                                                    }
                                                    info6.MethodSignature = info6.MethodSignature.Substring(0, info6.MethodSignature.Length - 1);
                                                    info6.MethodSignature = info6.MethodSignature + ")";
                                                }
                                                else
                                                {
                                                    info6.MethodSignature = info2.ServiceName + "." + info5.Name;
                                                }
                                                info6.JsonParams = this.paramsToJson(parameters);
                                                info4.MethodList.Add(info6);
                                            }
                                            else if (objArray3.Length > 0)
                                            {
                                                WebGetAttribute attribute2 = objArray3[0] as WebGetAttribute;
                                                info6 = new WcfMethodInfo
                                                {
                                                    HttpType = "GET",
                                                    Name = info5.Name
                                                };
                                                parameters = info5.GetParameters();
                                                info6.JsonParams = this.paramsToJson(parameters);
                                                info4.MethodList.Add(info6);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                AppDomain.Unload(domain);

            }
        }

        private string paramsToJson(ParameterInfo[] paramsList)
        {
            if (paramsList.Length == 0)
            {
                return "{}";
            }
            string str = "";
            foreach (ParameterInfo info in paramsList)
            {
                str = str + "," + this.getJson(info.Name, info.ParameterType);
            }
            if (str.Length > 0)
            {
                str = str.Substring(1);
            }
            return ("{" + str + "}");
        }

        private string getJson(string name, System.Type pi)
        {
            string str = "";
            if ((((pi == typeof(int)) || (pi == typeof(float))) || (pi == typeof(double))) || (pi == typeof(decimal)))
            {
                return string.Format("'{0}':0 ", name);
            }
            if ((((pi != typeof(int?)) && (pi != typeof(float?))) && (pi != typeof(double?))) && (pi != typeof(decimal?)))
            {
                if (pi == typeof(string))
                {
                    return string.Format("'{0}':''", name);
                }
                if (pi == typeof(bool))
                {
                    return string.Format("'{0}':false", name);
                }
                if (pi == typeof(bool?))
                {
                    return string.Format("'{0}':null", name);
                }
                if (pi == typeof(DateTime))
                {
                    return string.Format("'{0}':'{1}'", name, this.ConvertDateStringToJsonDate(DateTime.Now));
                }
                if (pi == typeof(DateTime?))
                {
                    return string.Format("'{0}':null", name);
                }
                if (pi.IsEnum)
                {
                    return string.Format("'{0}':0", name);
                }
                if (pi.IsArray || (pi == typeof(IEnumerable)))
                {
                    return string.Format("'{0}':[]", name);
                }
                if (pi.GetCustomAttributes(typeof(DataContractAttribute), false).Length > 0)
                {
                    string str2 = "";
                    foreach (PropertyInfo info in pi.GetProperties())
                    {
                        str2 = str2 + "," + this.getJson(info.Name, info.PropertyType);
                    }
                    if (str2.Length > 0)
                    {
                        str2 = str2.Substring(1);
                    }
                    str2 = "{" + str2 + "}";
                    return (str + string.Format("'{0}':{1}", name, str2));
                }
            }
            return string.Format("'{0}':null", name);
        }

        private string ConvertDateStringToJsonDate(DateTime dt)
        {
            dt = dt.ToUniversalTime();
            TimeSpan span = (TimeSpan)(dt - DateTime.Parse("1970-01-01"));
            return string.Format(@"\/Date({0}+0800)\/", span.TotalMilliseconds);
        }

        private string convertUrl(string main, string svc)
        {
            string str2 = svc.Replace(main, "").Replace(@"\", "/");
            if (str2.StartsWith("/"))
            {
                str2 = str2.Substring(1);
            }
            return str2;
        }

        private void createJSFile()
        {
            if (this.svcList.Count != 0)
            {
                string msg = "function 【0】() {\r\n    this.BaseAddress = '【1】';\r\n    this.getCookie = function (name, value, options) {\r\n        if (typeof value != 'undefined') { \r\n            options = options || {};\r\n            if (value === null) {\r\n                value = '';\r\n                options.expires = -1;\r\n            }\r\n            var expires = '';\r\n            if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {\r\n                var date;\r\n                if (typeof options.expires == 'number') {\r\n                    date = new Date();\r\n                    date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));\r\n                } else {\r\n                    date = options.expires;\r\n                }\r\n                expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE\r\n            }\r\n            var path = options.path ? '; path=' + options.path : '';\r\n            var domain = options.domain ? '; domain=' + options.domain : '';\r\n            var secure = options.secure ? '; secure' : '';\r\n            document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');\r\n        } else {\r\n            var cookieValue = null;\r\n            if (document.cookie && document.cookie != '') {\r\n                var cookies = document.cookie.split(';');\r\n                for (var i = 0; i < cookies.length; i++) {\r\n                    var cookie = jQuery.trim(cookies[i]);\r\n                    if (cookie.substring(0, name.length + 1) == (name + '=')) {\r\n                        cookieValue = decodeURIComponent(cookie.substring(name.length + 1));\r\n                        break;\r\n                    }\r\n                }\r\n            }\r\n            return cookieValue;\r\n        }\r\n    }\r\n    this.setUrlCookie = function (url, loginToken) {\r\n        var newUrl = url + '';\r\n        if (newUrl.indexOf('?') >= 0) {\r\n            newUrl = newUrl + '&LoginToken=' + loginToken;\r\n        } else {\r\n            newUrl = newUrl + '?LoginToken=' + loginToken;\r\n        }\r\n        return newUrl;\r\n    }\r\n    this.PostData = function (address, params, callback) {\r\n        var login = this.getCookie('LoginToken');\r\n        if (login == undefined || login == null || login == '') { throw '未登陆'; return; }\r\n        var options = { type: 'POST', url: '', dataType: 'jsonp', cache: false, data: {}, crossDomain: false, success: null, error: function (xhq, status, error) { } }\r\n        options.url = '/WcfProxy.cs?OriginalUrl=' + escape(this.setUrlCookie(this.BaseAddress + address, login));\r\n        options.data = { 'OriginalData': JSON.stringify(params) };\r\n        options.success = callback;\r\n        options.error = function (xhq, status, error){ \r\n            $.messager.alert('错误', '服务器错误代码[' + xhq.status + ']\\r\\n错误状态[' + status + ']\\r\\n错误信息[' + error + ']', 'error');\r\n        };\r\n        $.ajax(options);\r\n    };\r\n    this.GetData = function (address, params, callback) {\r\n        var login = this.getCookie('LoginToken');\r\n        if (login == undefined || login == null || login == '') { throw '未登陆'; return; }\r\n        var options = { type: 'get', url: '', contentType: 'application/json', dataType: 'jsonp', cache: false, data: {}, crossDomain: true, success: null, error: function (xhq, status, error) { } }\r\n        options.url = this.setUrlCookie(this.BaseAddress + address, login);\r\n        options.data = params;\r\n        options.success = callback;\r\n        options.error = function (xhq, status, error){ \r\n            $.messager.alert('错误', '服务器错误代码[' + xhq.status + ']\\r\\n错误状态[' + status + ']\\r\\n错误信息[' + error + ']', 'error');\r\n        };\r\n        $.ajax(options);\r\n    };\r\n}";
                msg = msg.Replace("【0】", this.projectName).Replace("【1】", this.domain);
                this.createJS(msg);
                foreach (SvcFileInfo info in this.svcList)
                {
                    string str2 = info.SvcFile.Substring(0, info.SvcFile.LastIndexOf('.'));
                    string str3 = info.SvcFile.Substring(info.SvcFile.LastIndexOf('.'));
                    foreach (ServiceInfo info2 in this.serviceList.list)
                    {
                        if (info2.ServiceName == info.ServiceName)
                        {
                            this.createJS(string.Format("", new object[0]));
                            this.createJS(string.Format("//由{0}文件提供的API", info.SvcFile), 0);
                            this.createJS(string.Format("{0}.SVC_{1} = function () {{ }}", this.projectName, str2));
                            foreach (EndPointInfo info3 in info2.EndPoints)
                            {
                                int num;
                                string str7;
                                string address = info3.Address;
                                if (address.StartsWith("/"))
                                {
                                    address = address.Substring(1);
                                }
                                if (address.EndsWith("/"))
                                {
                                    address = address.Substring(0, address.Length - 1);
                                }
                                string str5 = address.Replace("/", "_");
                                this.createJS(string.Format("{0}.SVC_{1}.Point_{2} = function () {{  }}", this.projectName, str2, str5), 0);
                                string str6 = info2.ServiceName.Replace(".", "_");
                                if (this.summary)
                                {
                                    this.createJS(string.Format("{0}.SVC_{1}.Point_{2}.{3} = function () {{", new object[] { this.projectName, str2, str5, str6 }), 0);
                                    List<string> classSummary = APISummary.GetClassSummary(info2.AssemblyName, info2.ServiceName);
                                    num = 0;
                                    while (num < classSummary.Count)
                                    {
                                        str7 = classSummary[num];
                                        if (num == 0)
                                        {
                                            this.createJS(string.Format("///<summary>{0}</summary>", str7), 1);
                                        }
                                        else
                                        {
                                            this.createJS(string.Format("///<para>{0}</para>", str7), 1);
                                        }
                                        num++;
                                    }
                                    this.createJS(string.Format("}}", new object[0]), 0);
                                }
                                else
                                {
                                    this.createJS(string.Format("{0}.SVC_{1}.Point_{2}.{3} = function () {{ }}", new object[] { this.projectName, str2, str5, str6 }), 0);
                                }
                                foreach (WcfMethodInfo info4 in info3.MethodList)
                                {
                                    this.createJS(string.Format("{0}.SVC_{1}.Point_{2}.{3}.{4} = function (data, callback) {{  ", new object[] { this.projectName, str2, str5, str6, info4.Name }), 0);
                                    if (this.summary)
                                    {
                                        List<string> methodSummary = APISummary.GetMethodSummary(info2.AssemblyName, info4.MethodSignature);
                                        if (methodSummary.Count > 1)
                                        {
                                            for (num = 0; num < methodSummary.Count; num++)
                                            {
                                                str7 = methodSummary[num];
                                                if (num == 0)
                                                {
                                                    this.createJS(string.Format("///<summary>{0}</summary>", str7), 1);
                                                }
                                                else
                                                {
                                                    this.createJS(string.Format("///<para>{0}</para>", str7), 1);
                                                }
                                            }
                                            this.createJS(string.Format("///</summary>", new object[0]), 1);
                                        }
                                        if (methodSummary.Count == 1)
                                        {
                                            this.createJS(string.Format("///<summary>{0}</summary>", methodSummary[0]), 1);
                                        }
                                    }
                                    this.createJS(string.Format("this.serviceUrl = \"{0}/{1}/{2}\";", info.SvcFile, address, info4.Name), 1);
                                    string str8 = this.defaultParams ? info4.JsonParams : "{}";
                                    this.createJS(string.Format("this.defaultParams={0};", str8), 1);
                                    this.createJS(string.Format("this.params = $.extend({{}}, this.defaultParams, data);", new object[0]), 1);
                                    this.createJS(string.Format("if ((data == undefined || data == null) && (callback == undefined || callback == null)) {{ ", this.projectName), 1);
                                    this.createJS(string.Format("var host = new {0}();", this.projectName), 2);
                                    this.createJS(string.Format("var baseUrl = host.BaseAddress + this.serviceUrl;", new object[0]), 2);
                                    this.createJS(string.Format("var token = host.getCookie('LoginToken');", new object[0]), 2);
                                    this.createJS(string.Format("return host.setUrlCookie(baseUrl, token);", new object[0]), 2);
                                    this.createJS(string.Format("}}", new object[0]), 1);
                                    if (info4.HttpType.ToUpper() == "POST")
                                    {
                                        this.createJS(string.Format("else {{ new {0}().PostData(this.serviceUrl, this.params, callback); }}", this.projectName), 1);
                                    }
                                    else if (info4.HttpType.ToUpper() == "GET")
                                    {
                                        this.createJS(string.Format("else {{ new {0}().GetData(this.serviceUrl, this.params, callback); }}", this.projectName), 1);
                                    }
                                    this.createJS(string.Format("}}", new object[0]), 0);
                                }
                            }
                        }
                    }
                    this.createJS(string.Format("", new object[0]));
                }
            }
        }

        private void createJS(string msg)
        {
            this.createJS(msg, 0);
        }

        private void createJS(string msg, int tabCount)
        {
            string str = "";
            for (int i = 0; i < tabCount; i++)
            {
                str = str + "   ";
            }
            this.json.Append(str + msg + "\r\n");
        }

        private string readFileText(string path)
        {
            FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read);
            StreamReader reader = new StreamReader(stream, Encoding.UTF8);
            string str = reader.ReadToEnd();
            reader.Close();
            stream.Close();
            return str;
        }

        private string getDomain(string csproj)
        {
            string input = this.readFileText(csproj);
            MatchCollection matchs = new Regex(@"<UseIIS>([\S]+?)</UseIIS>").Matches(input);
            string str2 = ((matchs.Count > 0) && (matchs[0].Groups.Count > 1)) ? matchs[0].Groups[1].Value : "";
            MatchCollection matchs2 = new Regex(@"<DevelopmentServerPort>([\d]+?)</DevelopmentServerPort>").Matches(input);
            string str3 = ((matchs2.Count > 0) && (matchs2[0].Groups.Count > 1)) ? matchs2[0].Groups[1].Value : "";
            MatchCollection matchs3 = new Regex(@"<IISUrl>([\S]+?)</IISUrl>").Matches(input);
            string str4 = ((matchs3.Count > 0) && (matchs3[0].Groups.Count > 1)) ? matchs3[0].Groups[1].Value : "";
            if (str2.ToLower() == "false")
            {
                if (string.IsNullOrEmpty(str3))
                {
                    return "";
                }
                return string.Format("http://localhost:{0}/", str3);
            }
            return str4;
        }


        public void AnalysisProject()
        {

            this.analysis(path);
        }

        private string getServiceName(string svcText)
        {
            MatchCollection matchs = new Regex("<%@[\\s]+ServiceHost[\\s\\S]+?Service=\"([\\S]+?)\"[\\s]+%>").Matches(svcText);
            if (matchs.Count > 0)
            {
                GroupCollection groups = matchs[0].Groups;
                if (groups.Count > 1)
                {
                    return groups[1].Value;
                }
                return "";
            }
            return "";
        }

        private List<FileInfo> getSvcFile(DirectoryInfo dir)
        {
            List<FileInfo> list = new List<FileInfo>();
            FileInfo[] files = dir.GetFiles("*.svc");
            list.AddRange(files);
            DirectoryInfo[] directories = dir.GetDirectories();
            if (directories.Length > 0)
            {
                for (int i = 0; i < directories.Length; i++)
                {
                    if (directories[i].Name.ToLower() != "obj")
                    {
                        List<FileInfo> collection = this.getSvcFile(directories[i]);
                        list.AddRange(collection);
                    }
                }
            }
            return list;
        }


        private void CopyHostToOutPath(string outpath)
        {
            if (!Directory.Exists(outpath)) Directory.CreateDirectory(outpath);

            string destfile = System.IO.Path.Combine(outpath, System.IO.Path.GetFileName(this.file));

            File.Copy(this.file, destfile, true);
        }

        public void Dispose()
        {

            GC.Collect();
        }
    }
}
